﻿using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Nyx.Scenes;
using Nyx.Xna;

namespace Nyx
{
    using XColor = Microsoft.Xna.Framework.Color;

    /// <summary>
    /// A scene editing and rendering control.
    /// </summary>
    public class SceneControl : XnaControl
    {
        Camera2D camera = new Camera2D();
        GridComponent gridComponent;
        bool isPanning;
        static Texture2D pixelTex;
        Vector2 previousMouseLocation;
        Scene scene;
        SpriteBatch spriteBatch;

        /// <summary>
        /// Initializes a new instance of the <see cref="SceneControl"/> class.
        /// </summary>
        public SceneControl()
            : base()
        {
            this.MouseDown += new MouseEventHandler(SceneControl_MouseDown);
            this.MouseMove += new MouseEventHandler(SceneControl_MouseMove);
            this.MouseUp += new MouseEventHandler(SceneControl_MouseUp);
            this.MouseWheel += new MouseEventHandler(SceneControl_MouseWheel);
        }

        /// <summary>
        /// Gets the camera.
        /// </summary>
        /// <value>The camera.</value>
        public Camera2D Camera
        {
            get { return camera; }
        }

        /// <summary>
        /// Gets the current scene.
        /// </summary>
        /// <value>The current scene.</value>
        public Scene CurrentScene
        {
            get { return scene; }
        }

        /// <summary>
        /// Gets the pixel texture.
        /// </summary>
        /// <value>The pixel texture.</value>
        public static Texture2D PixelTexture
        {
            get { return pixelTex; }
        }

        /// <summary>
        /// Sets the scene.
        /// </summary>
        /// <param name="scene">The scene.</param>
        public void SetScene(Scene scene)
        {
            this.scene = scene;
            gridComponent = new GridComponent(this.GraphicsDevice, scene.Width, scene.Height);
            camera.CenterOnScene(this.GraphicsDevice, scene);
        }

        /// <summary>
        /// Creates the scene.
        /// </summary>
        /// <param name="width">The width (in pixels).</param>
        /// <param name="height">The height (in pixels).</param>
        /// <returns>The created <see cref="Scene"/> object.</returns>
        public Scene CreateScene(int width, int height)
        {
            scene = new Scene(width, height);
            return scene;
        }

        /// <summary>
        /// Resets the camera.
        /// </summary>
        public void ResetCamera()
        {
            if (scene != null)
            {
                camera.Scale = Camera2D.DefaultScale;
                camera.CenterOnScene(this.GraphicsDevice, scene);
            }
        }

        /// <summary>
        /// Toggles the grid.
        /// </summary>
        /// <returns><c>true</c> if the grid is now enabled; otherwise, <c>false</c>.</returns>
        public bool ToggleGrid()
        {
            if (gridComponent != null)
            {
                gridComponent.Enabled = !gridComponent.Enabled;
                return gridComponent.Enabled;
            }
            return false;
        }

        /// <summary>
        /// Derived classes override this to initialize their drawing code.
        /// </summary>
        protected override void Initialize()
        {
            spriteBatch = new SpriteBatch(this.GraphicsDevice);
            pixelTex = new Texture2D(this.GraphicsDevice, 1, 1);
            XColor[] data = new XColor[1]
            {
                XColor.White,
            };
            pixelTex.SetData<XColor>(data);
            base.Initialize();
        }

        /// <summary>
        /// Renders the control.
        /// </summary>
        /// <param name="dt">The elapsed time since the previous render.</param>
        protected override void RenderXna(float dt)
        {
            this.GraphicsDevice.Clear(XColor.CornflowerBlue);
            this.RenderGrid();
            this.RenderScene();
            base.RenderXna(dt);
        }

        /// <summary>
        /// Updates the control.
        /// </summary>
        /// <param name="dt">The elapsed time since the previous update.</param>
        protected override void UpdateXna(float dt)
        {
            base.UpdateXna(dt);
        }

        void RenderGrid()
        {
            if (gridComponent != null && gridComponent.Enabled)
                gridComponent.Draw(camera.Transform);
        }

        void RenderScene()
        {
            if (scene != null)
            {
                spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, null, null, null, camera.Transform);
                scene.Render(spriteBatch);
                spriteBatch.End();
            }
        }

        void SceneControl_MouseDown(object sender, MouseEventArgs e)
        {
            previousMouseLocation = new Vector2(e.X, e.Y);
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                isPanning = true;
            }
        }

        void SceneControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (isPanning)
            {
                Vector2 mouseLocation = new Vector2(e.X, e.Y);
                Vector2 delta = mouseLocation - previousMouseLocation;
                camera.Position += delta;
                previousMouseLocation = mouseLocation;
            }
        }

        void SceneControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                isPanning = false;
            }
        }

        void SceneControl_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta > 0)
            {
                camera.Scale += .1f;
            }
            else if (e.Delta < 0)
            {
                camera.Scale -= .1f;
            }
        }
    }
}
